use super::*;

mod builder;
mod extracter;
mod tsfn;

pub use builder::*;
pub use extracter::*;
pub use tsfn::*;

/// Represents a JavaScript Symbol object
#[derive(Clone, Copy)]
pub struct Function<'val> {
	pub(crate) env: &'val Env,
	pub(crate) inner: sys::napi_value,
}

impl<'val> Function<'val> {
	// Creates a new function with the given name and callback
	pub fn new<'env>(env: &'env Env, name: &core::ffi::CStr, cb: sys::napi_callback) -> Result<'static, Function<'env>> {
		let mut ret = core::ptr::null_mut();
		unsafe { sys::napi_create_function(env.raw(), name.as_ptr(), name.count_bytes(), cb, core::ptr::null_mut(), &mut ret) }
			.result("napi_create_function")?;
		Ok(Function { env, inner: ret })
	}

	// Converts this Func to a Value type
	pub fn v(&'_ self) -> Value<'val> {
		Value::new(self.env, self.inner)
	}

	// Gets the function's name property
	pub fn get_name(&self) -> Result<'static, String> {
		self.v().get_named(c"name")?.to::<String>()
	}

	// Gets the function's length property (number of parameters)
	pub fn get_length(&self) -> Result<'static, i32> {
		self.v().get_named(c"length")?.to::<i32>()
	}

	// Calls the function with the given receiver and arguments
	pub fn call<This: IntoNapi, Ret: FromNapi<'val>, T: ArgsBuilder>(&'_ self, recv: Option<This>, args: T) -> Result<'static, Ret> {
		let args = args.into_args(self.env)?;
		let recv = recv.into_napi(self.env)?;
		self.call_raw(recv, &args)?.to()
	}

	// Internal implementation for function calling
	pub fn call_raw(&'_ self, recv: Value<'_>, args: &[sys::napi_value]) -> Result<'static, Value<'val>> {
		let mut ret = core::ptr::null_mut();
		unsafe { sys::napi_call_function(self.env.raw(), recv.raw(), self.inner, args.len(), args.as_ptr(), &mut ret) }
			.result("napi_call_function")?;
		Ok(Value::new(self.env, ret))
	}

	// Calls the function with no arguments
	pub fn call_no_args<This: IntoNapi, Ret: FromNapi<'val>>(&self, recv: Option<This>) -> Result<'static, Value<'_>> {
		self.call(recv, ())
	}
}

// Implementation for converting from Value to Func
impl<'val> FromNapi<'val> for Function<'val> {
	fn from_napi(v: Value<'val>) -> Result<'static, Self> {
		if v.is_function()? {
			Ok(Function {
				env: v.env,
				inner: v.inner,
			})
		} else {
			Err(Status::invalid_arg("value is not Func type"))
		}
	}
}

// Implementation for converting Func to Value
impl<'val> IntoNapi for Function<'val> {
	fn into_napi<'e>(self, env: &'e Env) -> Result<'static, Value<'e>> {
		if self.env.raw() != env.raw() {
			return Err(Status::invalid_arg("self.env not eq arg env"));
		}
		Ok(Value::new(env, self.inner))
	}
}
